﻿Imports Aricie.DNN.Modules.LuceneSearch.Business
Imports Aricie.DNN.Modules.LuceneSearch.Logs
Imports Lucene.Net.Index
Imports Lucene.Net.Search
Imports Aricie.ComponentModel
Imports Aricie.Collections
Imports Aricie.DNN.UI
Imports Aricie.DNN.Services
Imports Aricie.DNN.Modules.LuceneSearch.Settings
Imports Aricie.DNN.Settings
Imports DotNetNuke.Entities.Modules
Imports LSCommon = Aricie.DNN.Modules.LuceneSearch.Common
Imports System.Linq

Namespace Aricie.DNN.Modules.LuceneSearch.UI


    Public Class LuceneFilterContainer
        Inherits PlaceHolder
        Implements ILuceneFilterControl


        Public Event FilterChanged(ByVal sender As Object, ByVal e As GenericEventArgs(Of LuceneFilterQuery)) Implements ILuceneFilterControl.FilterChanged

        Private _FilterControls As List(Of ILuceneFilterControl)

        'Private _FieldNameDico As Dictionary(Of String, List(Of ILuceneFilterControl))

        'Private ReadOnly Property FieldNameDico() As Dictionary(Of String, List(Of ILuceneFilterControl))
        '    Get
        '        If _FieldNameDico Is Nothing Then
        '            _FieldNameDico = BuildFieldNamesDico()
        '        End If
        '        Return _FieldNameDico
        '    End Get
        'End Property

        Public ReadOnly Property FilterControls() As List(Of ILuceneFilterControl)
            Get
                If Me._FilterControls Is Nothing Then
                    Me._FilterControls = GetInnerFilterControls()
                End If
                Return Me._FilterControls
            End Get
        End Property

        Public Property MakeLog() As Boolean = False

        Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
            Me.HookUpEvents()
        End Sub

#Region "ILuceneFilterControl implements"

        Public Function GetFilter() As LuceneFilterQuery Implements ILuceneFilterControl.GetFilter

            Dim toReturn As New LuceneFilterQuery
            Dim filter As New BooleanQuery
            Dim search As New StringBuilder

            Dim objLog As FilterLogInfo = Nothing
            If MakeLog Then
                objLog = New FilterLogInfo(DateTime.Now, DnnContext.Current.User.UserID)
            End If


            For Each filterControl As ILuceneFilterControl In Me.FilterControls
                If filterControl.HasFilter Then
                    Dim objFilter As LuceneFilterQuery = filterControl.GetFilter()
                    If objFilter.FilterQuery IsNot Nothing Then
                        filter.Add(objFilter.FilterQuery, Occur.MUST)
                    End If
                    If objFilter.SearchQuery IsNot Nothing Then
                        search.Append(objFilter.SearchQuery)
                        search.Append(" ")
                    End If
                    If MakeLog AndAlso TypeOf filterControl Is LuceneFilterControlBase Then
                        Dim ctl As LuceneFilterControlBase = DirectCast(filterControl, LuceneFilterControlBase)
                        If ctl.EnableFilterLog Then
                            objLog.AddElement(ctl.GetFilterLogElement)
                        End If
                    End If
                End If
            Next
            If filter.Clauses.Count > 0 Then
                toReturn.FilterQuery = filter
            End If
            toReturn.SearchQuery = search.ToString.Trim
            If (toReturn.SearchQuery = String.Empty) Then
                toReturn.SearchQuery = Nothing ' on force la valeur à nul
            End If

            If MakeLog Then
                Dim pmb As PortalModuleBase = FormHelper.GetParentModuleBase(Me)
                Dim ctl As New FilterLogController(pmb.ModuleId)
                ctl.UpdateFilterLog(objLog)
            End If

            Return toReturn

        End Function


        Public Function HasFilter() As Boolean Implements ILuceneFilterControl.HasFilter
            Return FilterControls.Any(Function(ilfc) ilfc.HasFilter())
        End Function

        Public Function GetFieldNames() As IEnumerable(Of String) Implements ILuceneFilterControl.GetFieldNames
            Dim toReturn As New List(Of String)
            For Each filterControl As ILuceneFilterControl In Me.FilterControls
                toReturn.AddRange(filterControl.GetFieldNames())
            Next
            Return toReturn
        End Function

        Public Function ParseFilter(ByVal objFilter As LuceneFilterQuery) As Boolean Implements ILuceneFilterControl.ParseFilter
            ' TODO: actuellement chaque élément de la requête est passée à l'ensemble des filtres. 
            ' Je compte sur les filtres pour être intelligents et ne pas chercher à manger ce qu'ils n'ont pas de raison de vouloir
            ' Il se peut toutefois que ce ne soit pas le cas, il faudra faire une passe sur l'ensemble des filtres.
            ' TODO: on pourra aussi rebrancher l'optimisation via le dictionnaire si nécessaire, mais avant de récupérer cette optimisation il faudra 
            ' étudier son véritable impact, pas la peine d'avoir un code lourd et peu lisible si on gagne une poignée de picosecondes

            Dim FiltersUsedUrlQuery As Boolean = False

            If objFilter.HasInfo Then

                Dim BQ As BooleanQuery = TryCast(objFilter.FilterQuery, BooleanQuery)
                If BQ IsNot Nothing Then
                    ' on va travailler sur chacune des clauses présentes dans la requête booléenne et conserver celles que l'on doit effacer
                    Dim DeleteClauseList As New List(Of BooleanClause)

                    For Each clause As BooleanClause In BQ.Clauses
                        Dim LFQDummy As New LuceneFilterQuery With {.SearchQuery = String.Empty, .FilterQuery = clause.Query}
                        Dim ClauseUsedByFilter As Boolean = False
                        For Each ctl As ILuceneFilterControl In Me.FilterControls.Cast(Of LuceneFilterControlBase).Where(Function(lfcb) lfcb.QueryMode = QueryMode.Filter)
                            ' si au moins un contrôle indique qu'il connait cette clause, on va considérer que cette clause est à supprimer de la query initiale
                            ClauseUsedByFilter = ctl.ParseFilter(LFQDummy) OrElse ClauseUsedByFilter
                        Next
                        If ClauseUsedByFilter Then
                            DeleteClauseList.Add(clause)
                            FiltersUsedUrlQuery = True
                        End If
                    Next

                    For Each Clause As BooleanClause In DeleteClauseList
                        ' on supprime les clauses de la requête originelle puisque ces requêtes vont maintenant être prises en charge par les filtres
                        BQ.Clauses.Remove(Clause)
                    Next

                    If (BQ.GetClauses.Count = 0) Then
                        objFilter.FilterQuery = Nothing
                    End If
                End If

                Dim LFQDummySearchOnly As New LuceneFilterQuery With {.SearchQuery = objFilter.SearchQuery, .FilterQuery = Nothing}
                Dim SearchWasUsedUp As Boolean = False
                For Each ctl As ILuceneFilterControl In Me.FilterControls.Cast(Of LuceneFilterControlBase).Where(Function(lfcb) lfcb.QueryMode = QueryMode.Search)
                    ctl.ParseFilter(LFQDummySearchOnly)
                    SearchWasUsedUp = True
                Next

                If SearchWasUsedUp Then
                    objFilter.SearchQuery = Nothing
                    FiltersUsedUrlQuery = True
                End If

            End If
            Return FiltersUsedUrlQuery
        End Function

        Public Function GetCustomSortExpression() As IEnumerable(Of String) Implements ILuceneFilterControl.GetCustomSortExpression
            Dim toReturn As New List(Of String)
            For Each filterControl As ILuceneFilterControl In Me.FilterControls
                If Not filterControl.DisableAllCustomSortExpression Then
                    toReturn.AddRange(filterControl.GetCustomSortExpression())
                Else
                    toReturn = New List(Of String)
                    Exit For
                End If
            Next
            Return toReturn
        End Function

        Public Function DisableAllCustomSortExpression() As Boolean Implements ILuceneFilterControl.DisableAllCustomSortExpression
            Dim toReturn As Boolean = False
            For Each filterControl As ILuceneFilterControl In Me.FilterControls
                If filterControl.DisableAllCustomSortExpression Then
                    toReturn = True
                    Exit For
                End If
            Next
            Return toReturn
        End Function

#End Region

#Region "Private methods"

        Private Sub HookUpEvents()
            For Each ctl As ILuceneFilterControl In Me.FilterControls
                AddHandler ctl.FilterChanged, AddressOf Me.InnerControl_FilterChanged
            Next
        End Sub

        Protected Sub InnerControl_FilterChanged(ByVal sender As Object, ByVal e As GenericEventArgs(Of LuceneFilterQuery))
            RaiseEvent FilterChanged(Me, New GenericEventArgs(Of LuceneFilterQuery)(Me.GetFilter))
        End Sub

        'Private Function BuildFieldNamesDico() As SerializableDictionary(Of String, List(Of ILuceneFilterControl))

        '    Dim toReturn As New SerializableDictionary(Of String, List(Of ILuceneFilterControl))
        '    For Each filterControl As ILuceneFilterControl In Me.FilterControls

        '        Dim names As IEnumerable(Of String) = filterControl.GetFieldNames()
        '        For Each name As String In names
        '            If Not toReturn.ContainsKey(name) Then
        '                toReturn(name) = New List(Of ILuceneFilterControl)

        '            End If
        '            DirectCast(toReturn(name), List(Of ILuceneFilterControl)).Add(filterControl)
        '        Next

        '    Next
        '    Return toReturn

        'End Function

        Private Function GetInnerFilterControls() As List(Of ILuceneFilterControl)
            Dim toReturn As New List(Of ILuceneFilterControl)
            GetInnerFilterControls(Me, toReturn)
            Return toReturn
        End Function

        Private Sub GetInnerFilterControls(ByVal parentControl As Control, _
                                            ByRef filterControls As List(Of ILuceneFilterControl))
            For Each objControl As Control In parentControl.Controls
                If TypeOf objControl Is ILuceneFilterControl Then
                    filterControls.Add(CType(objControl, ILuceneFilterControl))
                Else
                    GetInnerFilterControls(objControl, filterControls)
                End If
            Next
        End Sub

#End Region


        Private _Enabled As Boolean = True
        Public Property Enabled() As Boolean Implements ILuceneFilterControl.Enabled
            Get
                Return _Enabled
            End Get
            Set(ByVal value As Boolean)
                For Each objFilter As ILuceneFilterControl In Me.FilterControls
                    objFilter.Enabled = value
                Next
                _Enabled = value
            End Set
        End Property


        Public Property EnableFilterLog As Boolean Implements ILuceneFilterControl.EnableFilterLog
            Get

            End Get
            Set(value As Boolean)

            End Set
        End Property

    End Class

End Namespace